package com.xdh.springcloud.security.jwt.common.factory;

import com.xdh.springcloud.common.message.Message;
import com.xdh.springcloud.common.util.UUIDUtil;
import com.xdh.springcloud.security.jwt.common.config.JwtConfig;
import com.xdh.springcloud.security.jwt.common.exception.JwtExpiredTokenException;
import com.xdh.springcloud.security.jwt.common.model.JwtToken;
import com.xdh.springcloud.security.jwt.common.model.UserModel;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtTokenFactory {

    @Autowired
    private JwtConfig jwtConfig;

    public JwtToken createAccessJwtToken(UserModel userModel) {
        if (!StringUtils.hasText(userModel.getUserId())) {
            throw new IllegalArgumentException("Cannot create JWT Token without userId");
        }
        ZoneId zoneId = ZoneId.systemDefault();
        Date now = Date.from(LocalDate.now().atStartOfDay(zoneId).toInstant());
        LocalDateTime currentTime = LocalDateTime.now();

        Map<String, Object> map = new HashMap<>();
        map.put("userName", userModel.getUserName());
        String token = Jwts.builder()
                .setId(UUIDUtil.random())
                .setIssuer("XDH_CLOUD")
                .setSubject(userModel.getUserId())
                .setIssuedAt(now)
                .setNotBefore(now)
                .addClaims(map)
                .setExpiration(Date.from(currentTime.plusSeconds(jwtConfig.getTokenExpirationSeconds()).atZone(zoneId).toInstant()))
                .signWith(SignatureAlgorithm.HS512, jwtConfig.getJwtSecretKeyBytes())
                .compact();
        return new JwtToken(token, jwtConfig.getTokenNextRefreshSeconds());
    }
//
//    public String createRefreshToken(UserModel userModel) {
//        if (StringUtils.hasText(userModel.getId())) {
//            throw new IllegalArgumentException("Cannot create JWT Token without username");
//        }
//
//        LocalDateTime currentTime = LocalDateTime.now();
//
//        Claims claims = Jwts.claims().setSubject(userModel.getUserName());
//        claims.put("scopes", Arrays.asList(Scopes.REFRESH_TOKEN.authority()));
//
//        String token = Jwts.builder()
//                .setClaims(claims)
//                .setIssuer(settings.getTokenIssuer())
//                .setId(UUID.randomUUID().toString())
//                .setIssuedAt(Date.from(currentTime.atZone(ZoneId.systemDefault()).toInstant()))
//                .setExpiration(Date.from(currentTime.plusMinutes(settings.getRefreshTokenExpTime()).atZone(ZoneId.systemDefault()).toInstant()))
//                .signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
//                .compact();
//
//        return new AccessJwtToken(token, claims);
//    }

    public Jws<Claims> parseClaims(String token) {
        try {
            return Jwts.parser().setSigningKey(jwtConfig.getJwtSecretKeyBytes()).parseClaimsJws(token);
        } catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) {
            throw new BadCredentialsException("Invalid JWT token: ", ex);
        } catch (ExpiredJwtException expiredEx) {
            throw new JwtExpiredTokenException("JWT Token expired", expiredEx);
        }
    }

    public JwtToken refreshJwtToken(Jws<Claims> claimsJws) {
        Claims body = claimsJws.getBody();
        UserModel userModel = new UserModel(body.getSubject(), body.get("userName", String.class));
        return createAccessJwtToken(userModel);
    }

    public Message createMessage(JwtToken jwtToken) {
        Message message = new Message();
        message.addData("token", jwtToken.getToken());
        message.addData("nextRefreshSeconds", jwtToken.getTokenNextRefreshSeconds());
        return message;
    }

    public Message createMessage(JwtToken jwtToken, Message message) {
        message.addData("token", jwtToken.getToken());
        message.addData("nextRefreshSeconds", jwtToken.getTokenNextRefreshSeconds());
        return message;
    }
}
